package com.dayu.finecomm.protocol.codec;

import java.util.Arrays;

import io.netty.buffer.ByteBuf;

/**
 * @ClassName: FixedLengthMagicDecoder
 * @Description:带长度的特定帧节的解码器，例如RPS8000： 0xAA 0x0xAA [帧头] 0xAB [长度] ... 0x88 0xDD
 *                                      [帧尾]
 * @author: 戴卫国
 * @date: 2018年2月23日 下午4:29:02
 * 
 * @param <T>
 * @Copyright: 2018 dayu Inc. All rights reserved.
 */
public abstract class FixedLengthCheckMagicDecoder<T> extends ProtocolDecoder<T> {

	byte[] startMagic;
	byte[] endMagic;

	int lenOffset;
	int lenBytes;

	int crcBytes;

	/***
	 * 长度字段是否指整帧长度
	 */
	boolean isFullFrame;

	/**
	 * 长度是否为BIGENDIAN
	 */
	boolean bigEndian;

	/**
	 * 
	 * @param startMagic
	 *            帧头
	 * @param endMagic
	 *            帧尾
	 * @param lenOffset
	 *            长度字节偏移（相对）
	 * @param lenBytes
	 *            长度字长
	 */
	public FixedLengthCheckMagicDecoder(byte[] startMagic, byte[] endMagic, int lenOffset, int lenBytes, int crcBytes) {

		this(startMagic, endMagic, lenOffset, lenBytes, crcBytes, false, false);
	}

	/**
	 * 
	 * @param startMagic
	 *            帧头
	 * @param endMagic
	 *            帧尾
	 * @param lenOffset
	 *            长度字节偏移（相对）
	 * @param lenBytes
	 *            长度字长
	 */
	public FixedLengthCheckMagicDecoder(String startMagic, String endMagic, int lenOffset, int lenBytes, int crcBytes) {
		this(startMagic.getBytes(), endMagic.getBytes(), lenOffset, lenBytes, crcBytes, false, false);
	}

	/**
	 * 
	 * @param startMagic
	 *            帧头
	 * @param endMagic
	 *            帧尾
	 * @param lenOffset
	 *            长度字节偏移（相对）
	 * @param lenBytes
	 *            长度字长
	 * @param bigEndian
	 *            长度是否为BIGENDIAN
	 */
	public FixedLengthCheckMagicDecoder(byte[] startMagic, byte[] endMagic, int lenOffset, int lenBytes, int crcBytes,
			boolean bigEndian) {

		this(startMagic, endMagic, lenOffset, lenBytes, crcBytes, false, bigEndian);
	}

	/**
	 * 
	 * @param startMagic
	 *            帧头
	 * @param endMagic
	 *            帧尾
	 * @param lenOffset
	 *            长度字节偏移（相对）
	 * @param lenBytes
	 *            长度字长
	 * @param bigEndian
	 *            长度是否为BIGENDIAN
	 */
	public FixedLengthCheckMagicDecoder(String startMagic, String endMagic, int lenOffset, int lenBytes, int crcBytes,
			boolean bigEndian) {

		this(startMagic.getBytes(), endMagic.getBytes(), lenOffset, lenBytes, crcBytes, false, bigEndian);
	}

	/**
	 * 
	 * @param startMagic
	 *            帧头
	 * @param endMagic
	 *            帧尾
	 * @param lenOffset
	 *            长度字节偏移（相对）
	 * @param lenBytes
	 *            长度字长
	 * @param crcBytes
	 *            校验字长
	 * @param isFullFrame
	 *            长度是否指整帧长度
	 * @param bigEndian
	 *            长度是否为BIGENDIAN
	 */
	public FixedLengthCheckMagicDecoder(byte[] startMagic, byte[] endMagic, int lenOffset, int lenBytes, int crcBytes,
			boolean isFullFrame, boolean bigEndian) {

		this.startMagic = startMagic;
		this.endMagic = endMagic;

		this.lenOffset = lenOffset;
		this.lenBytes = lenBytes;

		this.crcBytes = crcBytes;

		this.isFullFrame = isFullFrame;
		this.bigEndian = bigEndian;
	}

	/**
	 * 
	 * @param startMagic
	 *            帧头
	 * @param endMagic
	 *            帧尾
	 * @param lenOffset
	 *            长度字节偏移（相对）
	 * @param lenBytes
	 *            长度字长
	 * @param isFullFrame
	 *            长度是否指整帧长度
	 * @param bigEndian
	 *            长度是否为BIGENDIAN
	 */
	public FixedLengthCheckMagicDecoder(String startMagic, String endMagic, int lenOffset, int lenBytes, int crcBytes,
			boolean isFullFrame, boolean bigEndian) {

		this(startMagic.getBytes(), endMagic.getBytes(), lenOffset, lenBytes, crcBytes, false, bigEndian);
	}

	@Override
	public int checkStart(ByteBuf in) {

		return 0;
	}

	@Override
	public int check(ByteBuf in) {
		ByteBuf b1;
		while (in.readableBytes() > startMagic.length + endMagic.length) {

			b1 = in.readBytes(startMagic.length);

			if (Arrays.equals(b1.array(), startMagic)) {
				break;
			}

			in.skipBytes(1);
		}
		if (in.readableBytes() < startMagic.length + endMagic.length)
			return -1;

		// if (in.readableBytes() < startMagic.length)
		// return -1;
		//
		// ByteBuf b1 = in.readBytes(startMagic.length);
		//
		// if (!Arrays.equals(b1.array(), startMagic)) {
		// return -1;
		// }

		if (lenOffset > 0)
			in.skipBytes(lenOffset);

		if (in.readableBytes() < lenBytes)
			return -1;

		b1 = in.readBytes(lenBytes);

		int length = 0;

		switch (lenBytes) {
		case 1:
			length = b1.readUnsignedByte();
			break;
		case 2:

			if (bigEndian) {
				length = b1.readUnsignedByte() + b1.readUnsignedByte() * 256;
			} else {
				length = b1.readUnsignedShort();
			}
			break;
		case 3:
			if (bigEndian) {
				length = b1.readUnsignedByte() + b1.readUnsignedByte() * 256 + b1.readUnsignedByte() * 256 * 256;
			} else {
				length = b1.readUnsignedMedium();
			}
			break;
		case 4:
			if (bigEndian) {
				length = Integer.reverseBytes(b1.readInt());
			} else {
				length = b1.readInt();
			}
			break;
		}

		if (isFullFrame) {
			length = length - lenOffset + lenBytes;
		}

		if (in.readableBytes() < length)
			return -1;

		in.skipBytes(length);

		if (in.readableBytes() < crcBytes)
			return -1;

		in.skipBytes(crcBytes);

		if (in.readableBytes() < endMagic.length)
			return -1;

		b1 = in.slice(in.readerIndex(), endMagic.length).readBytes(endMagic.length);

		if (Arrays.equals(b1.array(), endMagic)) {

			return in.readerIndex() + endMagic.length;
		}

		return -1;
	}
}
